package com.starry.module.system.core.oauth2.authorization.provider;

import com.starry.core.common.constants.CommonConstant;
import com.starry.core.common.execption.ServiceException;
import com.starry.module.system.core.checkcode.service.CheckCodeService;
import com.starry.module.system.core.oauth2.authorization.constant.SecurityConstants;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Component;
import org.springframework.util.ObjectUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.Objects;

import static com.starry.core.common.constants.CodeType.ADMIN_LOGIN;
import static com.starry.core.common.constants.CodeType.APP_SMS_LOGIN;


/**
 * 验证码校验
 * 注入ioc中替换原先的DaoAuthenticationProvider
 * 在authenticate方法中添加校验验证码的逻辑
 * 最后调用父类的authenticate方法并返回
 *
 * @author 小柯
 */
@Slf4j
public class CaptchaAuthenticationProvider extends DaoAuthenticationProvider {

    private final CheckCodeService checkCodeService;

    /**
     * 利用构造方法在通过{@link Component}注解初始化时
     * 注入UserDetailsService和passwordEncoder，然后
     * 设置调用父类关于这两个属性的set方法设置进去
     *
     * @param userDetailsService 用户服务，给框架提供用户信息
     * @param passwordEncoder    密码解析器，用于加密和校验密码
     */
    public CaptchaAuthenticationProvider(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder, CheckCodeService checkCodeService) {
        this.checkCodeService = checkCodeService;
        super.setPasswordEncoder(passwordEncoder);
        super.setUserDetailsService(userDetailsService);
    }

    @Override
    public Authentication authenticate(Authentication authentication) throws AuthenticationException {
        // 获取当前request
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        if (requestAttributes == null) {
            throw new ServiceException("验证码检验失败");
        }
        HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest();

        // 获取当前登录方式
        String grantType = request.getParameter("grant_type");

        String loginType = request.getParameter(SecurityConstants.LOGIN_TYPE_NAME);

        // 内部认证
        if (Objects.equals(grantType, CommonConstant.INTERNAL_CLIENT_GRANT_TYPE)) {
            return super.authenticate(authentication);
        }

        // 第三方登录
        if (Objects.equals(grantType, SecurityConstants.GRANT_TYPE_THIRD)) {
            return super.authenticate(authentication);
        }

        // 会员密码登录暂时不用验证码
        if (Objects.equals(grantType, SecurityConstants.GRANT_TYPE_PASSWORD) && Objects.equals(loginType, SecurityConstants.MEMBER_LOGIN)) {
            return super.authenticate(authentication);
        }

        // 获取参数中的验证码
        String code = request.getParameter(SecurityConstants.CAPTCHA_CODE_NAME);

        if (ObjectUtils.isEmpty(code)) {
            throw new ServiceException("请输入验证码");
        }

        if (SecurityConstants.GRANT_TYPE_PASSWORD.equals(grantType)) {
            String captchaId = request.getParameter(SecurityConstants.CAPTCHA_ID_NAME);
            if (!checkCodeService.check(captchaId, code, ADMIN_LOGIN)) {
                throw new ServiceException("验证码不正确");
            }
        }

        if (SecurityConstants.GRANT_TYPE_SMS_CODE.equals(grantType)) {
            String phone = request.getParameter(SecurityConstants.OAUTH_SCHEMA_NAME_PHONE);
            // 会员登录
            if (Objects.equals(loginType, SecurityConstants.MEMBER_LOGIN)) {
                if (!checkCodeService.check(phone, code, APP_SMS_LOGIN)) {
                    throw new ServiceException("验证码不正确");
                }
            }

        }

        return super.authenticate(authentication);
    }
}